/*
 * 
 * Copyright 2010 David H Pollack
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 * 
 */

package com.dhpollack.rotogss.servlets;

import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import net.oauth.OAuthException;

import com.dhpollack.rotogss.data.AppConfig;
import com.dhpollack.rotogss.data.RotoGSSUsers;
import com.dhpollack.rotogss.data.YPlayers;
import com.dhpollack.rotogss.data.YahooGames;
import com.dhpollack.rotogss.helper.AppHelper;
import com.dhpollack.rotogss.helper.PMF;
import com.dhpollack.rotogss.helper.XPATH;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.simpleyql.Api;
import com.simpleyql.ApiFactory;
import com.simpleyql.QueryResult;

/**
 * @author davidpl
 *
 */
public class YStorePlayerServlet extends HttpServlet {
	
	private static final long serialVersionUID = 2317512496228980150L;

	private static final AppConfig appConfig = AppHelper.get();
	
	private static XPath xPath = XPATH.get().newXPath();
	
	private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	
	private Queue queue = QueueFactory.getDefaultQueue();
	
	private String user;
	
	private String gameKey;
	
	private String authData;
	
	private Integer startNum;
	
	private static final int PLAYERS_PER_CALL = 25;
	
	private static final int NUM_CALLS = 4;
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp) 
    		throws ServletException, IOException {
		doPost(req, resp);
	}
	
	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
    		throws ServletException, IOException {
		
		if((user = req.getParameter("user")) == null) {
			log("User Not Set");
			return;
		}
		
		if((gameKey = req.getParameter("game_key")) == null) {
			log("Game Key Not Set");
			return;
		}
		
		startNum = (req.getParameter("start") == null) ? 0 : Integer.valueOf(req.getParameter("start"));
		
		log("User / Game Key / Start: " + user + "/" + gameKey + "/" + startNum);
		
		if(!getUserAuthData()) {
			log("Authdata Not Found");
			return;
		}
		
		Key gKey = KeyFactory.createKey(YahooGames.class.getSimpleName(), Integer.valueOf(gameKey));
		
		// Get 50 players
		List<String> xmlList = new ArrayList<String>();
		for(int i=0;i<NUM_CALLS;i++) {
			String url = buildYahooUrl();
			startNum += PLAYERS_PER_CALL;
			xmlList.add(getPlayerXml(url));
		}

		List<String> xpathExps = new ArrayList<String>();
		xpathExps.add("player_key");
		xpathExps.add("name/first");
		xpathExps.add("name/last");
		xpathExps.add("name/full");
		xpathExps.add("name/ascii_first");
		xpathExps.add("name/ascii_last");
		xpathExps.add("editorial_player_key");
		xpathExps.add("editorial_team_key");
		xpathExps.add("editorial_team_full_name");
		xpathExps.add("editorial_team_abbr");
		xpathExps.add("uniform_number");
		xpathExps.add("display_position");
		xpathExps.add("image_url");
		xpathExps.add("is_undroppable");
		xpathExps.add("eligible_positions/position");
		xpathExps.add(".");

		List<Entity> players = new ArrayList<Entity>();
		
		try {
			List<XPathExpression> xpaths = createXPathList(xpathExps);
			for(String xml : xmlList) {
				NodeList playerNodes = parseYahooXml(xml, "//game/players/player");
				int nodeCount = playerNodes.getLength();
				log("Player Node Count: " + nodeCount);
				for(int i=0; i<nodeCount;i++) {
					Entity player = createYPlayer(playerNodes.item(i), gKey, xpaths);
					players.add(player);
				}
			}
		} catch (Exception e) {
			log("XML or XPath Exception");
			e.printStackTrace();
		}
		
		datastore.put(players);

		if(req.getParameter("total") != null) {
			Double total = Double.valueOf(req.getParameter("total"));
			Double times = Math.ceil(total / (NUM_CALLS*PLAYERS_PER_CALL));
			log("Call Script " + times + " times");
			
			for(int i=0;i<times.intValue();i++) {
				int startVal = startNum + (i)*(NUM_CALLS*PLAYERS_PER_CALL);
	            log("Adding to queue: " + user + "|" + gameKey + "|" + startVal);
				queue.add(url(req.getServletPath())
		            	 .param("user", user)
		            	 .param("game_key", gameKey)
		            	 .param("start", String.valueOf(startVal)));
			}

		}
		
	}

	private boolean getUserAuthData() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Key userKey = KeyFactory.createKey(RotoGSSUsers.class.getSimpleName(), user);
		RotoGSSUsers gss_user = null;
		try {
			gss_user = pm.getObjectById(RotoGSSUsers.class, userKey);
			authData = gss_user.getYahooOAuthData();
		} catch(JDOObjectNotFoundException e) {
			log("User Not Found In Datastore");
			return false;
		} finally {
			pm.close();
		}
		
		return true;
		
	}

	private String buildYahooUrl() {
		StringBuilder s = new StringBuilder();
		
		s.append("http://fantasysports.yahooapis.com/fantasy/v2/game/");
		s.append(gameKey);
		s.append("/players");
		if(startNum > 0) {
			s.append(";start=");
			s.append(startNum);
		}
		
		return s.toString();
	}

	private String getPlayerXml(String url) throws IOException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Api api = ApiFactory.getApiInstance(
				appConfig.getYahooConsumerKey(),
				appConfig.getYahooConsumerSecret(),
				appConfig.getBaseUrl() + "/simpleyqlcallback/",
	            false, null);
		
		QueryResult qr = null;
		
		try {
			qr = api.query2(url, authData);
		} catch (OAuthException e) {
			log("Error posting to Y!:", e);
			return null;
		} catch (RuntimeException e) {
			if (e.getMessage().equals("Unable to complete the HTTP request")) {
				if (e.getCause().getMessage()
						.contains("Timeout while fetching")) {
					log("Unable to complete Yahoo! query", e);
				} else {
					log("Error posting to Y!:", e);
					return null;
				}
			} else {
				throw e;
			}
		}
		
		// Update token, if needed (for future requests)
	    if (qr != null && !qr.getAuthdata().equals(authData)) {
	    	authData = qr.getAuthdata();
			Key userKey = KeyFactory.createKey(RotoGSSUsers.class.getSimpleName(), user);
	    	RotoGSSUsers gss_user = pm.getObjectById(RotoGSSUsers.class, userKey);
	    	gss_user.setYahooOAuthData(qr.getAuthdata());
	    	try {
	    		pm.makePersistent(gss_user);
	    	} finally {
	    		pm.close();
	    	}
	    }
	    
	    if(!pm.isClosed()) {
	    	pm.close();
	    }

		return qr.getText();
	}
	
	private List<XPathExpression> createXPathList(List<String> xpathExps) throws XPathExpressionException {
		List<XPathExpression> ret = new ArrayList<XPathExpression>();
		for(String xpathExp : xpathExps) ret.add(xPath.compile(xpathExp));
		
		return ret;
	}

	private NodeList parseYahooXml(String xml, String expression)
	throws SAXException, IOException, ParserConfigurationException, XPathExpressionException {
		InputSource is = new InputSource(new StringReader(xml));
		Document response = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
		NodeList n = (NodeList)xPath.evaluate( expression , response, XPathConstants.NODESET);
		return n;
	}

	private Entity createYPlayer(Node node, Key game_key, List<XPathExpression> xpaths) throws XPathExpressionException {
		String id = (String) xpaths.get(0).evaluate(node, XPathConstants.STRING);
		String firstName = (String) xpaths.get(1).evaluate(node, XPathConstants.STRING);
		String lastName = (String) xpaths.get(2).evaluate(node, XPathConstants.STRING);
		String fullName = (String) xpaths.get(3).evaluate(node, XPathConstants.STRING);
		String asciiFirst = (String) xpaths.get(4).evaluate(node, XPathConstants.STRING);
		String asciiLast = (String) xpaths.get(5).evaluate(node, XPathConstants.STRING);
		String ePK = (String) xpaths.get(6).evaluate(node, XPathConstants.STRING);
		String eTK = (String) xpaths.get(7).evaluate(node, XPathConstants.STRING);
		String eTFN = (String) xpaths.get(8).evaluate(node, XPathConstants.STRING);
		String eTA = (String) xpaths.get(9).evaluate(node, XPathConstants.STRING);
		String uniNum = (String) xpaths.get(10).evaluate(node, XPathConstants.STRING);
		String dispPos = (String) xpaths.get(11).evaluate(node, XPathConstants.STRING);
		String iUrl = (String) xpaths.get(12).evaluate(node, XPathConstants.STRING);
		String isDroppable = (String) xpaths.get(13).evaluate(node, XPathConstants.STRING);

		NodeList eliPosNodes = (NodeList) xpaths.get(14).evaluate(node, XPathConstants.NODESET);
		int eliPosCount = eliPosNodes.getLength();
		List<String> eliPos = new ArrayList<String>();
		for(int i=0;i < eliPosCount;i++) {
			String position = (String) xpaths.get(15).evaluate(eliPosNodes.item(i), XPathConstants.STRING);
			eliPos.add(position);
		}
		
		Entity e = new Entity(YPlayers.class.getSimpleName(), id);
		e.setProperty("gameKey", game_key);
		e.setProperty("firstName", firstName);
		e.setProperty("lastName", lastName);
		e.setProperty("fullName", fullName);
		e.setProperty("asciiFirstName", asciiFirst);
		e.setProperty("asciiLastName", asciiLast);
		e.setProperty("ePK", ePK);
		e.setProperty("eTK", eTK);
		e.setProperty("eTFN", eTFN);
		e.setProperty("eTA", eTA);
		if(uniNum.equals("00")) {
			e.setProperty("uniNum", -1);
		} else {
			e.setProperty("uniNum", Integer.valueOf(uniNum));
		}
		e.setProperty("dispPos", dispPos);
		e.setProperty("iUrl", iUrl);
		if(isDroppable.equals("0")) {
			e.setProperty("isDroppable", false);
		} else {
			e.setProperty("isDroppable", true);
		}
		e.setProperty("eliPos", eliPos);
		
		return e;
	}


}
